﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SQLite;

namespace KeyboardImageViewer
{
    public static class ImageQuery
    {
        private static bool filtersChanged = true;

        private static bool untaggedOnly;

        private static string search = "";
        private static bool searchFileName;
        private static bool searchTags;

        private static int minRating;

        private static string customClause = "";

        private static SortType sort = SortType.Random;
        private static bool ascending;

        private static string listName = "";

        public static bool UntaggedOnly
        {
            get { return untaggedOnly; }
            set
            {
                if (value != untaggedOnly)
                {
                    untaggedOnly = value;
                    MarkChanged();
                }
            }
        }

        public static string Search
        {
            get { return search; }
            set
            {
                if (value.ToLowerInvariant() != search)
                {
                    search = value.ToLowerInvariant();
                    MarkChanged();
                }
            }
        }

        public static bool SearchFileName
        {
            get { return searchFileName; }
            set
            {
                if (value != searchFileName)
                {
                    searchFileName = value;
                    MarkChanged();
                }
            }
        }

        public static bool SearchTags
        {
            get { return searchTags; }
            set
            {
                if (value != searchTags)
                {
                    searchTags = value;
                    MarkChanged();
                }
            }
        }

        public static int MinRating
        {
            get { return minRating; }
            set
            {
                if (value != minRating)
                {
                    minRating = value;
                    MarkChanged();
                }
            }
        }

        public static string CustomClause
        {
            get { return customClause; }
            set
            {
                if (value != customClause)
                {
                    customClause = value;
                    MarkChanged();
                }
            }
        }

        public static SortType Sort
        {
            get { return sort; }
            set
            {
                if (value != sort)
                {
                    sort = value;
                    MarkChanged();
                }
            }
        }

        public static bool Ascending
        {
            get { return ascending; }
            set
            {
                if (value != ascending)
                {
                    ascending = value;
                    MarkChanged();
                }
            }
        }

        public static string ListName
        {
            get { return listName; }
            set { listName = value; }
        }

        public static bool HasFilter
        {
            get
            {
                return search != "" || untaggedOnly || minRating != 0 || customClause != "";
            }
        }

        public static List<int> GetImageList(SQLiteConnection connection, bool forceRefresh)
        {
            if (forceRefresh)
            {
                filtersChanged = true;
            }

            return GetImageList(connection);
        }

        public static List<int> GetImageList(SQLiteConnection connection)
        {
            if (!filtersChanged)
            {
                return null;
            }

            var imageList = new List<int>();

            SQLiteCommand selectCommand = new SQLiteCommand(CreateSelectStatement(), connection);
            using (SQLiteDataReader reader = selectCommand.ExecuteReader())
            {
                while (reader.Read())
                {
                    imageList.Add(reader.GetInt32("id"));
                }
            }

            return imageList;
        }

        private static string CreateSelectStatement()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append("SELECT id FROM images WHERE included");

            var clauses = new List<string>();

            if (search != "" && (searchFileName || searchTags))
            {
                string[] searchTerms = search.Split(new char[] { ' ' });

                foreach (string rawSearchTerm in searchTerms)
                {
                    bool exclude = false;

                    string searchTerm = rawSearchTerm.Trim();
                    searchTerm = searchTerm.Replace("'", "");
                    searchTerm = searchTerm.Replace("|", "");

                    if (searchTerm != "" && searchTerm[0] == '-')
                    {
                        exclude = true;
                        searchTerm = searchTerm.Substring(1, searchTerm.Length - 1);
                    }

                    if (searchTerm == "")
                    {
                        continue;
                    }

                    if (!exclude)
                    {
                        if (searchFileName && searchTags)
                        {
                            clauses.Add("(tags LIKE '%|" + searchTerm + "|%' OR dependentTags LIKE '%|" + searchTerm + "|%' OR name LIKE '%" + searchTerm + "%')");
                        }
                        else if (searchFileName)
                        {
                            clauses.Add("name LIKE '%" + searchTerm + "%'");
                        }
                        else if (searchTags)
                        {
                            clauses.Add("(tags LIKE '%|" + searchTerm + "|%' OR dependentTags LIKE '%|" + searchTerm + "|%')");
                        }
                    }
                    else
                    {
                        if (searchFileName && searchTags)
                        {
                            clauses.Add("tags NOT LIKE '%|" + searchTerm + "|%' AND dependentTags NOT LIKE '%|" + searchTerm + "|%' AND name NOT LIKE '%" + searchTerm + "%'");
                        }
                        else if (searchFileName)
                        {
                            clauses.Add("name NOT LIKE '%" + searchTerm + "%'");
                        }
                        else if (searchTags)
                        {
                            clauses.Add("tags NOT LIKE '%|" + searchTerm + "|%' AND dependentTags NOT LIKE '%|" + searchTerm + "|%'");
                        }
                    }
                }
            }

            if (untaggedOnly)
            {
                clauses.Add("(tags ISNULL OR tags = '')");
            }

            if (minRating != 0)
            {
                if (minRating == -1)
                {
                    clauses.Add("(rating ISNULL OR rating = 0)");
                }
                else
                {
                    clauses.Add("rating >= " + minRating);
                }
            }

            if (customClause != "")
            {
                clauses.Add(customClause);
            }

            if (clauses.Count > 0)
            {
                foreach (string clause in clauses)
                {
                    builder.Append(" AND ");
                    builder.Append(clause);
                }
            }

            string orderClause = "";

            string ascString;

            if (ascending)
            {
                ascString = "ASC";
            }
            else
            {
                ascString = "DESC";
            }

            switch (sort)
            {
                case SortType.Random:
                    orderClause = " ORDER BY RANDOM()";
                    break;
                case SortType.Modified:
                    orderClause = " ORDER BY lastWriteTime " + ascString;
                    break;
                case SortType.Name:
                    orderClause = " ORDER BY name " + ascString;
                    break;
                default:
                    break;
            }

            builder.Append(orderClause);

            return builder.ToString();
        }

        private static void MarkChanged()
        {
            filtersChanged = true;
            listName = "";
        }
    }
}
